﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace crdmcgui
{
    [Serializable]
    public abstract class DataManagerContainer
    {
        protected string _name;
        public abstract string Name { get; set; }
        public abstract string Comment { get; set; }
        public int GroupCount
        {
            get { return Groups.Count; }
        }
        public int RulesetCount
        {
            get { return this.Rulesets.Count; }
        }

        public SortableBindingList<RulesetGroup> Groups { get; set; }
        public SortableBindingList<Ruleset> Rulesets { get; set; }

        public void AddGroup(RulesetGroup rsgGroup)
        {
            this.Groups.Add(rsgGroup);
        }
        public void AddGroupRange(IEnumerable<RulesetGroup> items)
        {
            for (int i = 0; i < items.Count(); i++)
            {
                AddGroup(items.ElementAt(i));
            }
        }
        public void RemoveGroupAt(int index)
        {
            Groups.RemoveAt(index);
        }
        public bool RemoveGroup(RulesetGroup item)
        {
            return Groups.Remove(item);
        }
        public void InsertGroup(int index, RulesetGroup item)
        {
            Groups.Insert(index, item);
        }
        public bool ContainsGroup(RulesetGroup item)
        {
            return Groups.Contains(item);
        }
        public int IndexOfGroup(RulesetGroup item)
        {
            return Groups.IndexOf(item);
        }

        public bool ContainsRuleset(Ruleset item)
        {
            return Rulesets.Contains(item);
        }
        public bool RemoveRuleset(Ruleset item)
        {
            return Rulesets.Remove(item);
        }
        public void RemoveRulesetAt(int index)
        {
            Rulesets.RemoveAt(index);
        }
        public void InsertRuleset(int index, Ruleset item)
        {
            Rulesets.Insert(index, item);
        }
        public int IndexOfRuleset(Ruleset item)
        {
            return Rulesets.IndexOf(item);
        }
        public void AddRuleset(Ruleset rsRuleset)
        {
            Rulesets.Add(rsRuleset);
        }

        public DataManagerContainer()
        {
            Clear();
        }

        public virtual String[] Compile()
        {
            List<string> tmpLines = new List<string>();

            for (int i = 0; i < GroupCount; i++)
            {
                //compile the groups
                tmpLines.AddRange(Groups[i].Compile());
            }

            for (int i = 0; i < RulesetCount; i++)
            {
                tmpLines.AddRange(Rulesets[i].Compile());
            }

            return tmpLines.ToArray();
        }

        protected int ParseGroup(string[] strArray, int nLineStart, DataManagerContainer Parent)
        {
            int nReturn = nLineStart;
            RulesetGroup newGroup = new RulesetGroup();
            string[] tmpNameAndComment = GetNameAndComment(strArray[nReturn]);
            if (tmpNameAndComment.Length > 0) newGroup.Name = tmpNameAndComment[0].Trim(); ;
            if (tmpNameAndComment.Length > 1) newGroup.Comment = tmpNameAndComment[1].Trim();

            nReturn++;

            //parses until it reaches the end of the group and returns the line number after the end of the group
            while (nReturn < strArray.Length && IdentifyLineType(strArray[nReturn]) != LineParseType.GroupEndLine)
            {
                string[] tmpVariable = new string[0];
                switch (IdentifyLineType(strArray[nReturn]))
                {
                    case LineParseType.GroupStartLine:
                        nReturn = ParseGroup(strArray, nReturn, newGroup);
                        break;
                    case LineParseType.RulesetLine:
                    case LineParseType.RulesetNameLine:
                        nReturn = ParseRuleset(strArray, nReturn, newGroup);
                        break;
                    case LineParseType.GroupEndLine:
                        return nReturn;
                    default:
                        break;
                }
                nReturn++;
            }
            Parent.AddGroup(newGroup);

            return nReturn;
        }

        public int MergeGroup(string[] strArray, int nLineStart, DataManagerContainer Parent)
        {
            int nReturn = nLineStart;
            RulesetGroup newGroup = new RulesetGroup();
            string[] tmpNameAndComment = GetNameAndComment(strArray[nReturn]);
            if (tmpNameAndComment.Length > 0) newGroup.Name = tmpNameAndComment[0].Trim(); ;
            if (tmpNameAndComment.Length > 1) newGroup.Comment = tmpNameAndComment[1].Trim();

            IEnumerable<RulesetGroup> oldGroup = Parent.Groups.Where(x => x.Name == newGroup.Name);

            if (oldGroup.Count() > 0) 
            {
                newGroup = oldGroup.ElementAt(0);
            }
            else
            {
                Parent.AddGroup(newGroup);
            }
            nReturn++;

            //parses until it reaches the end of the group and returns the line number after the end of the group
            while (nReturn < strArray.Length && IdentifyLineType(strArray[nReturn]) != LineParseType.GroupEndLine)
            {
                string[] tmpVariable = new string[0];
                switch (IdentifyLineType(strArray[nReturn]))
                {
                    case LineParseType.GroupStartLine:
                        nReturn = MergeGroup(strArray, nReturn, newGroup);
                        break;
                    case LineParseType.RulesetLine:
                    case LineParseType.RulesetNameLine:
                        nReturn = MergeRuleset(strArray, nReturn, newGroup);
                        break;
                    case LineParseType.GroupEndLine:
                        return nReturn;
                    default:
                        break;
                }
                nReturn++;
            }

            return nReturn;
        }

        protected string[] GetNameAndComment(string string0)
        {
            return string0.Split(new string[] { Global.GroupHeader, Global.RulesetNameHeader, Global.CommentHeader }, 2, StringSplitOptions.RemoveEmptyEntries);
        }

        /// <summary>
        /// Parses Orphaned Ruleset
        /// </summary>
        public int ParseRuleset(string[] strArray, int nLineStart, DataManagerContainer rsgParent)
        {
            int nReturn = nLineStart;
            //parses until it reaches the end of the group and returns the line number after the end of the group
            string strName = "";
            string strComment = "";
            Ruleset newRS = new Ruleset();

            while (nReturn < strArray.Length)
            {
                string tmpString = strArray[nReturn];
                LineParseType tmpLPT = IdentifyLineType(tmpString);
                if (tmpLPT == LineParseType.RulesetNameLine)
                {
                    string[] tmpNameAndComment = GetNameAndComment(tmpString);
                    strName = tmpNameAndComment[0].Trim();
                    if (tmpNameAndComment.Length > 1) strComment = tmpNameAndComment[1].Trim();
                    nReturn++;
                    newRS = new Ruleset(strArray[nReturn]);
                    newRS.Name = strName;
                    newRS.Comment = strComment;
                    if (!rsgParent.Rulesets.Contains(newRS))
                    { rsgParent.AddRuleset(newRS); }
                    break;
                }
                else if (IdentifyLineType(strArray[nReturn]) == LineParseType.RulesetLine)
                {
                    newRS.Name = strName;
                    newRS.Comment = strComment;
                    strName = strComment = "";
                    newRS.Parse(tmpString);
                    rsgParent.AddRuleset(newRS);
                    break;
                }
                nReturn++;
            }
            return nReturn;
        }

        public int MergeRuleset(string[] strArray, int nLineStart, DataManagerContainer rsgParent)
        {
            int nReturn = nLineStart;
            //parses until it reaches the end of the group and returns the line number after the end of the group
            string strName = "";
            string strComment = "";
            Ruleset newRS = new Ruleset();

            while (nReturn < strArray.Length)
            {
                string tmpString = strArray[nReturn];
                LineParseType tmpLPT = IdentifyLineType(tmpString);
                if (tmpLPT == LineParseType.RulesetNameLine)
                {
                    string[] tmpNameAndComment = GetNameAndComment(tmpString);
                    strName = tmpNameAndComment[0].Trim();
                    if (tmpNameAndComment.Length > 1) strComment = tmpNameAndComment[1].Trim();
                    nReturn++;
                    newRS = new Ruleset(strArray[nReturn]);
                    newRS.Name = strName;
                    newRS.Comment = strComment;
                    if (!rsgParent.Rulesets.Contains(newRS))
                    { rsgParent.AddRuleset(newRS); }
                    break;
                }
                else if (IdentifyLineType(strArray[nReturn]) == LineParseType.RulesetLine)
                {
                    newRS.Name = strName;
                    newRS.Comment = strComment;
                    strName = strComment = "";
                    newRS.Parse(tmpString);
                    if (!rsgParent.Rulesets.Contains(newRS))
                    { rsgParent.AddRuleset(newRS); }
                    break;
                }
                nReturn++;
            }
            return nReturn;
        }


        public virtual void Clear()
        {
            this._name = "";
            this.Comment = "";
            this.Groups = new SortableBindingList<RulesetGroup>();
            this.Rulesets = new SortableBindingList<Ruleset>();
        }

        public bool Contains(RulesetGroup item)
        {
            return ContainsGroup(item);
        }

        public static LineParseType IdentifyLineType(string strLine)
        {
            LineParseType lptReturn = LineParseType.BlankLine;
            if (strLine.StartsWith("#@"))
            {
                if (strLine.StartsWith(Global.GroupHeader))
                {
                    lptReturn = LineParseType.GroupStartLine;
                }
                else if (strLine.StartsWith(Global.EndOfGroup))
                {
                    lptReturn = LineParseType.GroupEndLine;
                }
                else if (strLine.StartsWith(Global.RulesetNameHeader))
                {
                    lptReturn = LineParseType.RulesetNameLine;
                }
                else if (strLine.StartsWith(Global.VariableHeader))
                {
                    lptReturn = LineParseType.SetVariableLine;
                }
                else if (strLine.StartsWith(Global.EndOfRules))
                {
                    lptReturn = LineParseType.EndRulesLine;
                }
                else if (strLine.StartsWith(Global.NotesHeader))
                {
                    lptReturn = LineParseType.NotesStartLine;
                }
                else if (strLine.StartsWith(Global.EndOfNotes))
                {
                    lptReturn = LineParseType.NotesEndLine;
                }
                else if (strLine.StartsWith(Global.AuthorHeader))
                {
                    lptReturn = LineParseType.AuthorLine;
                }
            }
            else if (string.IsNullOrEmpty(strLine) || strLine == "#")
            {
                lptReturn = LineParseType.BlankLine;
            }
            else if (strLine.StartsWith("# -"))
            {
                lptReturn = LineParseType.DividerLine;
            }
            else if (strLine.StartsWith("<<"))
            {
                lptReturn = LineParseType.RulesetLine;
            }

            return lptReturn;
        }


    }
}
